var qrcode = {
  imagedata: null,
  width: 0,
  height: 0,
  qrCodeSymbol: null,
  debug: false,
  maxImgSize: 1024 * 1024,
};

qrcode.decode = function (obj) {
  obj.resolve = obj.resolve || function () {
  };
  obj.reject = obj.reject || function () {
  };
  if (!obj.src) {
    var canvas_qr = document.getElementById("qr-canvas");
    var context = canvas_qr.getContext('2d');
    qrcode.width = canvas_qr.width;
    qrcode.height = canvas_qr.height;
    qrcode.imagedata = context.getImageData(0, 0, qrcode.width, qrcode.height);
    qrcode.result = process({
      width: canvas_qr.width,
      height: canvas_qr.height,
      data: qrcode.imagedata.data,
      context: context
    });
    obj.resolve(qrcode.result);
    return qrcode.result;
  } else if (typeof Image != 'undefined') {
    var image = new Image();
    image.crossOrigin = "Anonymous";
    image.onload = function()  {
      var canvas_qr = document.createElement('canvas');
      var context = canvas_qr.getContext('2d');
      var nheight = image.height;
      var nwidth = image.width;
      if (image.width * image.height > qrcode.maxImgSize) {
        var ir = image.width / image.height;
        nheight = Math.sqrt(qrcode.maxImgSize / ir);
        nwidth = ir * nheight;
      }
      canvas_qr.width = nwidth;
      canvas_qr.height = nheight;
      context.drawImage(image, 0, 0, canvas_qr.width, canvas_qr.height);
      qrcode.width = canvas_qr.width;
      qrcode.height = canvas_qr.height;
      try {
        qrcode.imagedata = context.getImageData(0, 0, canvas_qr.width, canvas_qr.height);
      } catch (e) {
        console.log(e);
        obj.reject('Cross domain image reading not supported in your browser! Save it to your computer then drag and drop the file!');
        return;
      }
      try {
        obj.resolve(process({
          width: nwidth,
          height: nheight,
          data: qrcode.imagedata.data,
          context: context
        }))
      } catch (e) {
        console.log(e);
        obj.reject('error decoding QR Code')
        return
      }
    }
    image.src = obj.src;
  } else {
    const getPixels = require('get-pixels');
    getPixels(obj.src, function(err, pixels)  {
      if (err) {
        console.log(err)
        return
      }
      try {
        obj.resolve(process({
          width: pixels.shape[0],
          height: pixels.shape[1],
          data: pixels.data
        }))
      } catch (e) {
        console.log(e);
        obj.reject('error decoding QR Code')
        return
      }
    })
  }
}

qrcode.isUrl = function (s) {
  var regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
  return regexp.test(s);
}

qrcode.decode_url = function (s) {
  var escaped = "";
  try {
    escaped = escape(s);
  } catch (e) {
    console.log(e);
    escaped = s;
  }
  var ret = "";
  try {
    ret = decodeURIComponent(escaped);
  } catch (e) {
    console.log(e);
    ret = escaped;
  }
  return ret;
}

qrcode.decode_utf8 = function (s) {
  if (qrcode.isUrl(s))
    return qrcode.decode_url(s);
  else
    return s;
}

function process(image) {
  var start = new Date().getTime(),
    ctx = image.context,
    debug = image.debug,
    imagedata = image.data;
  image = grayScaleToBitmap(grayscale(image));
  if (debug) {
    var point = 0
    for (var y = 0; y < qrcode.height; y++) {
      for (var x = 0; x < qrcode.width; x++) {
        imagedata.data[point] = image.data[x + y * qrcode.width] ? 0 : 0;
        imagedata.data[point + 1] = image.data[x + y * qrcode.width] ? 0 : 0;
        imagedata.data[point + 2] = image.data[x + y * qrcode.width] ? 255 : 0;
        point += 4
      }
    }
    ctx.putImageData(imagedata, 0, 0);
  }
  const Detector = require('./Detector');
  var detector = new Detector(image);
  var qRCodeMatrix = detector.detect();
  if (debug)
    ctx.putImageData(imagedata, 0, 0);
  const Decoder = require('./Decoder');
  var reader = Decoder.decode(qRCodeMatrix.bits);
  var data = reader.DataByte;
  var str = "";
  for (var i = 0; i < data.length; i++) {
    for (var j = 0; j < data[i].length; j++)
      str += String.fromCharCode(data[i][j]);
  }
  var end = new Date().getTime();
  var time = end - start;
  console.log(time);
  return qrcode.decode_utf8(str);
}

qrcode.getPixel = function (x, y) {
  if (qrcode.width < x) {
    throw "point error";
  }
  if (qrcode.height < y) {
    throw "point error";
  }
  var point = (x * 4) + (y * qrcode.width * 4);
  var p = (qrcode.imagedata.data[point] * 33 + qrcode.imagedata.data[point + 1] * 34 + qrcode.imagedata.data[point + 2] * 33) / 100;
  return p;
}

qrcode.binarize = function (th) {
  var ret = new Array(qrcode.width * qrcode.height);
  for (var y = 0; y < qrcode.height; y++) {
    for (var x = 0; x < qrcode.width; x++) {
      var gray = qrcode.getPixel(x, y);

      ret[x + y * qrcode.width] = gray <= th ? true : false;
    }
  }
  return ret;
}

function getMiddleBrightnessPerArea(image) {
  var numSqrtArea = 4;
  //obtain middle brightness((min + max) / 2) per area
  var areaWidth = Math.floor(qrcode.width / numSqrtArea);
  var areaHeight = Math.floor(qrcode.height / numSqrtArea);
  var minmax = new Array(numSqrtArea);
  for (var i = 0; i < numSqrtArea; i++) {
    minmax[i] = new Array(numSqrtArea);
    for (var i2 = 0; i2 < numSqrtArea; i2++) {
      minmax[i][i2] = new Array(0, 0);
    }
  }
  for (var ay = 0; ay < numSqrtArea; ay++) {
    for (var ax = 0; ax < numSqrtArea; ax++) {
      minmax[ax][ay][0] = 0xFF;
      for (var dy = 0; dy < areaHeight; dy++) {
        for (var dx = 0; dx < areaWidth; dx++) {
          var target = image.data[areaWidth * ax + dx + (areaHeight * ay + dy) * qrcode.width];
          if (target < minmax[ax][ay][0])
            minmax[ax][ay][0] = target;
          if (target > minmax[ax][ay][1])
            minmax[ax][ay][1] = target;
        }
      }
      //minmax[ax][ay][0] = (minmax[ax][ay][0] + minmax[ax][ay][1]) / 2;
    }
  }
  var middle = new Array(numSqrtArea);
  for (var i3 = 0; i3 < numSqrtArea; i3++) {
    middle[i3] = new Array(numSqrtArea);
  }
  for (var ay = 0; ay < numSqrtArea; ay++) {
    for (var ax = 0; ax < numSqrtArea; ax++) {
      middle[ax][ay] = Math.floor((minmax[ax][ay][0] + minmax[ax][ay][1]) / 2);
      //Console.out.print(middle[ax][ay] + ",");
    }
    //Console.out.println("");
  }
  //Console.out.println("");
  return middle;
}

function grayScaleToBitmap(grayScale) {
  var middle = getMiddleBrightnessPerArea(grayScale);
  var sqrtNumArea = middle.length;
  var areaWidth = Math.floor(grayScale.width / sqrtNumArea);
  var areaHeight = Math.floor(grayScale.height / sqrtNumArea);
  var bitmap = {
    width: grayScale.width,
    height: grayScale.height,
    data: new Array(grayScale.height * grayScale.width)
  };
  var index;
  for (var ay = 0; ay < sqrtNumArea; ay++) {
    for (var ax = 0; ax < sqrtNumArea; ax++) {
      for (var dy = 0; dy < areaHeight; dy++) {
        for (var dx = 0; dx < areaWidth; dx++) {
          index = areaWidth * ax + dx + (areaHeight * ay + dy) * qrcode.width;
          if (grayScale.data[index] < middle[ax][ay]) {
            bitmap.data[index] = true;
          } else {
            bitmap.data[index] = false;
          }
        }
      }
    }
  }
  return bitmap;
}

function grayscale(image) {
  var ret = {
      width: image.width,
      height: image.height,
      data: new Array(image.width * image.height)
    },
    point = 0,
    _point = 0;
  for (var y = 0; y < image.height; y++) {
    for (var x = 0; x < image.width; x++) {
      ret.data[_point] = (image.data[point] * 33 + image.data[point + 1] * 34 + image.data[point + 2] * 33) / 100;
      point += 4;
      _point++;
    }
  }
  return ret;
}

Array.prototype.remove = function (from, to) {
  var rest = this.slice((to || from) + 1 || this.length);
  this.length = from < 0 ? this.length + from : from;
  return this.push.apply(this, rest);
};

module.exports = qrcode
